import logging
import os
import re
import subprocess
import threading
import time
import traceback
import typing
from queue import Queue
from logging.handlers import RotatingFileHandler

import serial
import serial.tools
import serial.tools.list_ports
import usb.util



class UsbSerialLinDevice(object):
    def __init__(self, sn_number:str, is_debug:bool=False) -> None:
        # 类内辅助成员变量
        self._sn_number:str = sn_number                                    # 设备序列号
        self._thread_send_data:threading.Thread = None                     # 发送数据的线程
        self._thread_receive_data:threading.Thread = None                  # 接收数据的线程
        self._thread_handle_receive_data:threading.Thread = None           # 处理接收数据的线程
        self._hid_fd:int = 0                                               # 设备文件描述符
        self._send_queue:Queue = Queue(1000)                               # 发送队列
        self._receive_queue:Queue = Queue(1000)                            # 接收队列
        self._is_running:bool = False                                      # 运行状态
        
        # 线程事件
        self._event_cmd_send_slave_data = threading.Event()                # 发送从机数据事件
        self._event_cmd_start_device = threading.Event()                   # 启动设备事件
        
        # logger 相关
        current_dir = os.getcwd()
        self._is_debug:bool = is_debug
        self._logger = logging.getLogger(__name__)
        formatter = logging.Formatter('[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]:%(message)s')
        handler1 = logging.StreamHandler()
        handler1.setFormatter(formatter)
        handler2 = RotatingFileHandler(os.path.join(current_dir, __name__ + '.log'), maxBytes=1024*1024*10, backupCount=5, encoding='utf-8')
        handler2.setFormatter(formatter)
        self._logger.addHandler(handler1)
        self._logger.addHandler(handler2)
        if self._is_debug:
            self._logger.setLevel(logging.DEBUG)
        else:
            self._logger.setLevel(logging.INFO)
        self._logger.info("UsbSerialLinDevice 初始化, sn_number: {0}".format(self._sn_number))

    def start(self) -> None:
        self._find_device()
        self._is_running = True
        self._thread_send_data = threading.Thread(target=self._thread_send_data_func)
        self._thread_receive_data = threading.Thread(target=self._thread_receive_data_func)
        self._thread_handle_receive_data = threading.Thread(target=self._thread_handle_receive_data_func)
        self._thread_send_data.start()
        self._thread_receive_data.start()
        self._thread_handle_receive_data.start()
        self._start_device()
    
    def stop(self) -> None:
        self._is_running = False
        os.close(self._hid_fd)
    
    def cmd_send_slave_data(self, is_enable:int, id:int, check_mode:int, dlc:int, data:typing.List[int]) -> bool:
        """设置从节点响应

        Args:
            is_enable (bool): 从响应是否使能，0:不使能 1:使能
            id (int): LIN 节点 ID
            check_mode (int): 校验模式 0:标准 1:增强
            dlc (int): 数据长度
            data (typing.List[int]): 设置响应的数据

        Returns:
            bool: 是否设置成功
        """
        # 00:01:02:03:04:05:06:07:08:09:10:11:12:13:14:15:16:17:18:19:20:21:22:23:24:25:26:27:28:29:30:31
        # 55:AA:10:00:00:10:00:01:22:00:08:E0:D6:D5:19:BD:64:91:56:4F:E7:77:24:FF:00:00:00:00:00:00:C6:88
        # 55:AA:02:00:01:00:01:01:80:25:00:00:E8:03:00:00:DA:25:00:00:00:00:00:00:00:00:00:00:00:00:93:88
        # 55:AA:10:00:00:10:00:01:22:01:08:B1:0E:46:29:0B:D6:08:81:00:00:00:00:00:00:00:00:00:00:00:E3:88
        # 55:AA:10:00:00:10:00:01:22:01:08:BC:E9:49:54:35:B4:7C:9A:67:00:00:00:00:00:00:00:00:00:00:F3:88
        # 55:AA:10:00:00:10:00:01:22:01:08:E0:D6:D5:19:BD:64:91:56:D2:FB:3B:5C:09:00:00:00:00:00:00:64:88
        # for j in range(5):
        cmd_map = {}
        start_pos = 11
        current_ms:int = int(time.time() * 1000) & 0xFFFFFFFF
        # current_ms:int = 1000
        for pos, i in enumerate(data):
            if (pos > 7):
                break
            cmd_map[start_pos] = i
            start_pos += 1
        cmd_map.update({
            2: 0x10,
            3: 0x00,
            4: 0x00,
            5: 0x10,
            6: 0x00,
            7: 0x01,
            8: id,
            9: check_mode,
            10: dlc,
            19: self._cal_check_sum(data, self._id_parity_query(id)) if (check_mode == 1) else self._cal_check_sum(data, 0x00),
            20: (current_ms >> 0) & 0xFF,
            21: (current_ms >> 8) & 0xFF,
            22: (current_ms >> 16) & 0xFF,
            23: (current_ms >> 24) & 0xFF
        })
        send_data = self._build_cmd(cmd_map)
        self._send_data(send_data)
        return True

    def cmd_start_device(self, communication_control:int = 0x01) -> None:
        """_summary_

        Args:
            communication_control (int): 0:关闭 HID 通信 1:开启 HID 通信
        """
        data = self._build_cmd({
            2: 0x01,
            4: communication_control
        })
        self._send_data(data)

    def cmd_config_filter(self, filter:int = 0xFFFFFFFFFFFFFFFF) -> bool:
        """配置 LIN 节点的过滤器

        Args:
            filter (int): _description_

        Returns:
            bool: _description_
        """
        res = True
        data = {
            2: 0x03,
            4: (filter >> 0) & 0xFF,
            5: (filter >> 1) & 0xFF,
            6: (filter >> 2) & 0xFF,
            7: (filter >> 3) & 0xFF,
            8: (filter >> 4) & 0xFF,
            9: (filter >> 5) & 0xFF,
            10: (filter >> 6) & 0xFF,
            11: (filter >> 7) & 0xFF
        }
        self._send_data(self._build_cmd(data))
        return res

    def _get_dev_path_by_sn(self, sn_number:str) -> str:
        """根据设备的 SN 号获得设备的 dev_path

        Args:
            sn_number (str): 设备的 SN 号

        Returns:
            str: 设备的 dev_path，如 /dev/hidraw0
        """
        res = ""
        dev_list = self._get_usb_serial_lin_dev_list()
        for dev_info in dev_list:
            if (dev_info["serial_number"] == sn_number):
                res = dev_info["dev_path"]
        return res

    def _get_usb_serial_lin_dev_list(self) -> typing.List[dict]:
        """获得当前电脑上的 HID 设备类型的 USB LIN 设备列表

        Args:
            sn_number (str): 设备的 SN 号

        Returns:
            typing.List[dict]: 存放设备信息的列表，返回的格式范例为：
            [
                {
                    "serial_number": "SN12345678",
                    "usb_address": "/usb0/001-001/001-001.0:",
                    "dev_path": "/dev/hidraw0"
                },
            ]
        """
        r_l = []
        usb_dev_name_map_sn = dict()
        udev_path_map_hid_path = dict()
        id_vender = 0x1993
        id_product = 0x2021
        
        try:
            # 构建 usb_dev_name_map_sn
            devices = usb.core.find(find_all=True, idVendor=id_vender, idProduct=id_product)
            for device in devices:
                usb_dev_name = ""
                serial_number = usb.util.get_string(device, device.iSerialNumber)
                bus_number:str = str(device.bus)
                port_number:str = str(device.port_numbers[0])
                port_numbers:tuple = device.port_numbers
                if (len(port_numbers) == 1):
                    # 一般直接连接在电脑上
                    usb_dev_name = "/usb{0}/{1}-{2}/{3}-{4}:".format(
                        bus_number, bus_number, port_number, bus_number, port_number)
                elif (len(port_numbers) == 2):
                    # 连接在USB hub上
                    usb_dev_name = "/usb{0}/{1}-{2}/{3}-{4}.{5}/{6}-{7}.{8}:".format(
                        bus_number, bus_number, port_numbers[0], bus_number, port_numbers[0], port_numbers[1], bus_number, port_numbers[0], port_numbers[1])
                else:
                    raise Exception("还不支持构建大于 2 个 port_numbers 的设备")
                usb_dev_name_map_sn[usb_dev_name] = serial_number
            
            # 构建 udev_path_map_hid_path
            res = subprocess.run(["find", "/dev", "-name", "hidraw*"], capture_output=True, text=True, check=True).stdout.strip()
            dev_path_list = res.split("\n")
            for hid_dev in dev_path_list:
                res = subprocess.run(["udevadm", "info", "--name=" + hid_dev], capture_output=True, text=True, check=True).stdout.strip()
                partern = r"P: .*usb.*/.*1993:2021.*" + hid_dev.split("/")[-1]
                matches= re.findall(partern, res)
                assert len(matches) == 1
                res:str = matches[0]
                udev_path = res.replace("P: ", "", 1).strip()
                # self._logger.debug("udev_path:", udev_path)
                udev_path_map_hid_path[udev_path] = hid_dev
            
            # 对比两个 dict 构建映射关系
            for usb_dev_name, serial_number in usb_dev_name_map_sn.items():
                for udev_path, hid_path in udev_path_map_hid_path.items():
                    if (usb_dev_name in udev_path):
                        r_l.append({
                            "serial_number": serial_number,
                            "usb_address": usb_dev_name,
                            "dev_path": hid_path
                        })
                        break
            
            # 返回 hwinfo 的输出内容
            return r_l
        except Exception as e:
            # 捕获其他可能的异常
            self._logger.debug("Exception occurred while getting hid device list:\n {0}".format(traceback.format_exc()))
            return r_l
    
    def _bytes_to_hex_str(self, data:bytes) -> str:
        """将 bytes 转为 hex 字符串

        Args:
            data (bytes): 要转换的 bytes

        Returns:
            str: 转换后的 hex 字符串
        """
        return ":".join("{:02X}".format(x) for x in data)
        
    def _build_cmd(self, bytes_position_map_data:typing.Dict[int, int]) -> typing.List[int]:
        """构建要发送的数据

        Args:
            bit_map_data (dict): byte 位置和数据之间的映射关系 范例
            {
                12: 0x04,
                3: 0x66
            }

        Returns:
            typing.List[int]: 构造好的数据
        """
        data = [0x00] * 64
        # 填充m默认的数据
        data[0]  = 0x55
        data[1]  = 0xAA
        data[31] = 0x88
        data[32] = 0x55
        data[33] = 0xAA
        data[62] = 0xFF
        data[63] = 0x88
        for byte_pos, byte_data in bytes_position_map_data.items():
            # if (byte_data <  or byte_data > )
            if (byte_pos >=0 and byte_pos < 30):
                data[byte_pos] = byte_data
        data[30] = sum(data[0:30]) & 0xFF
        return data
        
    def _cal_check_sum(self, data:typing.List[int], id:int) -> int:
        """计算校验和

        Args:
            id (int): LIN 节点 ID
            data (typing.List[int]): 要计算的校验数据

        Returns:
            int: 计算出的校验和
        """
        checksum_data_length = len(data)
        # parity_id = self._id_parity_query(id)
        
        checksum = 0x00
        for i in range(len(data)):
            checksum += data[i]
            if (checksum > 0xFF):
                checksum = (checksum + 1) & 0xFF
            else:
                checksum = checksum & 0xFF
        checksum += id
        if (checksum > 0xFF):
            checksum = (checksum + 1) & 0xFF
        else:
            checksum = checksum & 0xFF
        # self._logger.debug("计算校验和取反之前的值为: {0:02X}".format(checksum))
        
        checksum_res = 0x00
        for i in range(8):
            if (checksum & (1 << i)):
                continue
            checksum_res |= (1 << i)
        # self._logger.debug("计算校验和取反之后的值为: {0:02X}".format(checksum_res))
        return checksum_res
    
    def _id_parity_query(self, id:int) -> int:
        """_summary_

        Args:
            id (int): _description_

        Returns:
            int: _description_
        """
        id_hard_parity = [
            0x80,0xC1,0x42,0x03,0xC4,0x85,0x06,0x47,
            0x08,0x49,0xCA,0x8B,0x4C,0x0D,0x8E,0xCF,
            0x50,0x11,0x92,0xD3,0x14,0x55,0xD6,0x97,
            0xD8,0x99,0x1A,0x5B,0x9C,0xDD,0x5E,0x1F,
            0x20,0x61,0xE2,0xA3,0x64,0x25,0xA6,0xE7,
            0xA8,0xE9,0x6A,0x2B,0xEC,0xAD,0x2E,0x6F,
            0xF0,0xB1,0x32,0x73,0xB4,0xF5,0x76,0x37,
            0x78,0x39,0xBA,0xFB,0x3C,0x7D,0xFE,0xBF,
        ]
        if (id > 0x3F):
            return 0xFF
        
        return id_hard_parity[id]
    
    def _find_device(self) -> None:
        """初始化设备
        """
        dev_path = ""
        
        dev_path = self._get_dev_path_by_sn(self._sn_number)
        if (not dev_path):
            self._logger.error("设备序列号 {0} 未找到对应的设备路径".format(self._sn_number))
            return
        
        self._logger.info("UsbSerialLinDevice 初始化, dev_path: {0}".format(dev_path))
        os.system("chmod 777 " + dev_path)
        self._hid_fd = os.open(dev_path, os.O_RDWR)
        
    def _thread_send_data_func(self) -> None:
        """从队列中取出数据并写到设备中
        """
        while (self._is_running):
            data:bytes = None
            try:
                data = self._send_queue.get(block=True, timeout=1)
            except:
                pass
            if (not isinstance(data, bytes)):
                continue
            os.write(self._hid_fd, data)
            self._logger.debug("电脑发送: {0}".format(self._bytes_to_hex_str(data)))
        self._logger.debug("_thread_send_data_func is stopped.")
    
    def _thread_receive_data_func(self) -> None:
        while (self._is_running):
            data = None
            data = os.read(self._hid_fd, 64)  # 读取 1024 字节数据
            # self._logger.debug("receive data: {0}".format(data))
            if (not data):
                continue
            self._receive_queue.put(data)
            self._logger.debug("设备回复: {0}".format(self._bytes_to_hex_str(data)))
        self._logger.debug("_thread_receive_data_func is stopped.")
    
    def _thread_handle_receive_data_func(self) -> None:
        while (self._is_running):
            data = None
            try:
                data = self._receive_queue.get(block=True, timeout=1)
            except:
                pass
            if (data):
                # 将 bytes 转为 int list
                i_l = [int(x) for x in data]
                self._cb_on_receive_data(i_l)
        self._logger.debug("_thread_handle_receive_data_func is stopped.")
    
    def _send_data(self, data:typing.List[int]) -> None:
        # 将 int list 转为 bytes
        # self._cal_sum(data)
        
        data_bytes = bytes(data)
        self._send_queue.put(data_bytes)
    
    def _cb_on_receive_data(self, data:typing.List[int]) -> None:
        #  00:01:02:03:04:05:06:07:08:09:10:11:12:13:14:15:16:17:18:19:20:21:22:23:24:25:26:27:28:29:30:31
        #  55:AA:02:00:01:00:01:01:80:25:00:00:E8:03:00:00:DE:25:00:00:00:00:00:00:00:00:00:00:00:00:97:88
        # bytes 转 hex 字符串
        # self._cal_sum(data)
        hex_str = ":".join("{:02x}".format(x) for x in data)
        # self._logger.debug("receive data: {0}".format(hex_str))
        # TODO 这里开始处理各种情况

    def _on_recv_heart_beat(
        self, 
        heart_beat_type:int, 
        node_type:int, 
        error_frame_shield:int, 
        send_display_state:int, 
        recv_display_state:int, 
        bitrate:int, 
        heart_beat_time:int, 
        measure_bitrate:int, 
        delate_config_state:int) -> None:
        """当收到心跳报文的时候做什么

        Args:
            heart_beat_type (int): 0:普通心跳 1:强制上位机更新心跳
            node_type (int): 0:主节点 1:从节点
            error_frame_shield (int): 0:不屏蔽错误帧 1:屏蔽错误帧
            send_display_state (int): 0:关闭发送显示 1:打开发送显示
            recv_display_state (int): 0:关闭接收显示 1:打开接收显示
            bitrate (int): 波特率
            heart_beat_time (int): 心跳时间
            measure_bitrate (int): 测量波特率
            delate_config_state (int): 已删除配置操作
        """
        self._logger.debug("收到心跳报文: heart_beat_type: {0}, node_type: {1}, send_display_state: {2}, recv_display_state: {3}, bitrate: {4}, heart_beat_time: {5}, measure_bitrate: {6}, delate_config_state: {7}".format(
            heart_beat_type, node_type, send_display_state, recv_display_state, bitrate, heart_beat_time, measure_bitrate, delate_config_state))
        pass
    
    def _on_recv_device_start(self, communication_state:int, device_version:int, app_version:int) -> None:
        """当收到设备启动的返回数据后做什么

        Args:
            communication_state (int): 通信状态
            device_version (int): 设备硬件描述
            app_version (int): 设备固件版本
        """
        pass
    
    def _on_recv_data(self, id:int, check_mode:int, dlc:int, data:typing.List[int]) -> None:
        """当收到 LIN 数据的时候做什么

        Args:
            id (int): 节点 ID
            check_mode (int): 校验模式
            dlc (int): 数据长度
            data (typing.List[int]): 数据
        """
        pass
    
    def _start_device(self) -> None:
        """启动设备
        """
        self.cmd_start_device(0x01)
        time.sleep(0.1)
        self.cmd_config_filter()
    